package rldungeon.help

import net.cyndeline.scalarlib.rldungeon.dgs.graph.{PointlessAreaData, Room}
import net.cyndeline.scalarlib.rldungeon.grammar.util.VertexFactory

/**
 * Used in graph tests with rooms.
 */
class RoomVertex(override val id: Int,
                 val roomSize: Int,
                 val distance: Int,
                 val data: Option[PointlessAreaData[RoomVertex]],
                 val holdsRewards: Boolean,
                 val holdsActivators: Boolean,
                 val pointlessStatus: Boolean,
                 val roomProductions: Vector[RoomVertex],
                 val isStart: Boolean,
                 val isGoal: Boolean,
                 val activators: Vector[Int],
                 val rewards: Vector[Int],
                 val objects: Vector[Int]) extends Room[RoomVertex](id, distance, data, pointlessStatus) {

  /* Controls the output when creating new rooms. Advisable not to rely on this when multiple method calls are made
   * of the same room.
   */
  val factory = new CustomRoomFactory(roomProductions)

  /**
   * A meaningful vertex that can hold rewards and activators with no pointless data.
   */
  def this(id: Int) = this(id, 0, 0, None, true, true, false, Vector(), false, false, Vector(), Vector(), Vector())

  def this(id: Int, roomProduct: RoomVertex, ps: RoomVertex*) = this(id, 0, 0, None, true, true, false, Vector(roomProduct) ++ ps, false, false, Vector(), Vector(), Vector())

  def this(id: Int, pointless: Boolean, data: PointlessAreaData[RoomVertex], roomProduct: Vector[RoomVertex]) = this(id, 0, 0, Option(data), true, true, pointless, roomProduct, false, false, Vector(), Vector(), Vector())

  def this(id: Int, data: PointlessAreaData[RoomVertex]) = this(id, 0, 0, Option(data), true, true, false, Vector(), false, false, Vector(), Vector(), Vector())

  /**
   * Creates a pointless node that can or can not hold rewards.
   */
  def this(id: Int, data: PointlessAreaData[RoomVertex], canHoldRewards: Boolean) = this(id, 0, 0, Option(data), canHoldRewards, true, false, Vector(), false, false, Vector(), Vector(), Vector())

  /*
   * Class-specific methods
   */

  def setStartStatus(s: Boolean) = factory.make(this.id, this.roomSize, this.distanceToGoal, this.pointlessAreaData, this.canHoldAdditionalRewards, this.canHoldAdditionalActivators, this.pointlessStatus, this.roomProductions, s, this.isGoal, this.activators, this.rewards, this.objects)

  def setGoalStatus(s: Boolean) = factory.make(this.id, this.roomSize, this.distanceToGoal, this.pointlessAreaData, this.canHoldAdditionalRewards, this.canHoldAdditionalActivators, this.pointlessStatus, this.roomProductions, isStart, s, this.activators, this.rewards, this.objects)

  def setHoldRewardStatus(status: Boolean): RoomVertex = factory.make(this.id, this.roomSize, this.distanceToGoal, this.pointlessAreaData, status, this.canHoldAdditionalActivators, this.pointlessStatus, this.roomProductions, this.isStart, this.isGoal, this.activators, this.rewards, this.objects)

  def setHoldActivatorStatus(status: Boolean): RoomVertex = factory.make(this.id, this.roomSize, this.distanceToGoal, this.pointlessAreaData, this.canHoldAdditionalRewards, status, this.pointlessStatus, this.roomProductions, this.isStart, this.isGoal, this.activators, this.rewards, this.objects)

  def addReward(id: Int) = factory.make(this.id, this.roomSize, this.distanceToGoal, this.pointlessAreaData, this.canHoldAdditionalRewards, this.pointlessStatus, this.pointlessStatus, this.roomProductions, this.isStart, this.isGoal, this.activators, rewards :+ id, this.objects)

  def addActivator(id: Int) = factory.make(this.id, this.roomSize, this.distanceToGoal, this.pointlessAreaData, this.canHoldAdditionalRewards, this.pointlessStatus, this.pointlessStatus, this.roomProductions, this.isStart, this.isGoal, activators :+ id, this.rewards, this.objects)

  def addObject(id: Int) = factory.make(this.id, this.roomSize, this.distanceToGoal, this.pointlessAreaData, this.canHoldAdditionalRewards, this.pointlessStatus, this.pointlessStatus, this.roomProductions, this.isStart, this.isGoal, this.activators, this.rewards, objects :+ id)

  override def toString: String = "RoomV:[" + id + "]" + "<" + (if (!holdsRewards) " no_rewards" else "") + (if (!holdsActivators) " no_activators" else "") + (if (pointlessStatus) " pointless" else "") + ">"

  /**
   * Produces a copy of this room with a specified distance to the goal.
   * @param d Distance to the goal from this room.
   * @return A copy of this room with the specified goal distance.
   */
  override def withDistanceToGoal(d: Int): RoomVertex = factory.make(this.id, this.roomSize, d, this.pointlessAreaData, canHoldAdditionalRewards, canHoldAdditionalActivators, this.pointlessStatus, this.roomProductions, this.isStart, this.isGoal, this.activators, this.rewards, this.objects)

  /**
   * Produces a copy of this room with a set pointless area data object.
   * @param d Pointless area data.
   * @return A copy of this room with the specified pointless area data.
   */
  override def withPointlessData(d: PointlessAreaData[RoomVertex]): RoomVertex = factory.make(this.id, this.roomSize, this.distanceToGoal, Option(d), canHoldAdditionalRewards, canHoldAdditionalActivators, this.pointlessStatus, this.roomProductions, this.isStart, this.isGoal, this.activators, this.rewards, this.objects)

  /**
   * Checks if the room can hold additional rewards. Used when dealing with pointless areas.
   * @return True if the room can hold additional rewards, otherwise false.
   */
  def canHoldAdditionalRewards: Boolean = holdsRewards

  /**
   * Checks if the room can hold additional activators. Used when dealing with pointless areas.
   * @return True if the room can hold additional activators, otherwise false.
   */
  def canHoldAdditionalActivators: Boolean = holdsActivators
  /**
   * Produces a copy of this room with its pointless status set.
   * @param pointless True if this room is deemed pointless, otherwise false.
   * @return A copy of this room with its pointless status set.
   */
  override def withPointlessStatus(pointless: Boolean): RoomVertex = factory.make(this.id, this.roomSize, this.distanceToGoal, this.data, this.canHoldAdditionalRewards, this.canHoldAdditionalActivators, pointless, this.roomProductions, this.isStart, this.isGoal, this.activators, this.rewards, this.objects)

  override def equals(other: Any): Boolean = other match {
    case r: RoomVertex => (r.id == id
      && r.roomSize == roomSize
      && r.data == data
      && r.holdsRewards == holdsRewards
      && r.holdsActivators == holdsActivators
      && r.pointlessStatus == pointlessStatus
      && r.roomProductions == roomProductions
      && r.isStart == isStart
      && r.isGoal == isGoal
      && r.activators == activators
      && r.rewards == rewards)
  }

  override def hashCode: Int = id ^ roomSize ^ data.## ^ holdsRewards.## ^ holdsActivators.## ^ pointlessStatus.## ^ roomProductions.## ^ isStart.## ^ isGoal.## ^ activators.## ^ rewards.##
}

trait RoomFactory {
  def make(id: Int,
           roomSize: Int,
           distance: Int,
           data: Option[PointlessAreaData[RoomVertex]],
           holdsRewards: Boolean,
           holdsActivators: Boolean,
           pointlessStatus: Boolean,
           roomProductions: Vector[RoomVertex],
           startStatus: Boolean,
           goalStatus: Boolean,
           activators: Vector[Int],
           rewards: Vector[Int],
           objects: Vector[Int]): RoomVertex
}

/**
 * A room factory that lets you load up pre-created rooms in a list with the order they'll be produced in.
 */
class CustomRoomFactory(rooms: Vector[RoomVertex]) extends RoomFactory {
  var roomsToProduce = rooms

  def this() = this(Vector[RoomVertex]())

  def make(id: Int,
           roomSize: Int,
           distance: Int,
           data: Option[PointlessAreaData[RoomVertex]],
           holdsRewards: Boolean,
           holdsActivators: Boolean,
           pointlessStatus: Boolean,
           roomProductions: Vector[RoomVertex],
           startStatus: Boolean,
           goalStatus: Boolean,
           activators: Vector[Int],
           rewards: Vector[Int],
           objects: Vector[Int]): RoomVertex = {
    if (!roomsToProduce.isEmpty) {
      val r = roomsToProduce.head
      roomsToProduce = roomsToProduce.drop(1)
      r
    } else {
      new RoomVertex(id, roomSize, distance, data, holdsRewards, holdsActivators, pointlessStatus, roomProductions, startStatus, goalStatus, activators, rewards, objects)
    }
  }
}

/**
 * Used when creating productions.
 */
class RoomVertexFactory extends VertexFactory[RoomVertex] {
  private var id = 0
  def produce: RoomVertex = {
    val r = new RoomVertex(id)
    id += 1
    r
  }
}
